/**
 * \file Array.h
 * \brief Array definition and declaration class
 * \author Edouard ROGE
 * \date 20 decembre 2010
 */
#ifndef _ARRAY_
#define _ARRAY_

#include "Core/Log/LogManager.h"
#include "ArrayBase.h"

/**
 * \class Array
 * \brief Array class with template, similar to std::vector<T>
 */
template < class T >
class Array : public ArrayBase<T>
{
public:

	/** Array<T>::Iterator */
	typedef T*			Iterator;

	/** Array<T>::ConstIterator */
	typedef const T*	ConstIterator;

	/**
	 * \fn Array();
	 * \brief Default constructor
	 */
	Array();

	/**
	 * \fn Array(uint32 _iSize);
	 * \param _iSize new array's size
	 * \brief constructor, Resize array with the size in param
	 */
	Array(uint32 _iSize);

	/**
	 * \fn Array(const Array<T> & _tArray);
	 * \brief Copy constructor
	 */
	Array(const Array<T> & _tArray);

	/**
	 * \fn ~Array();
	 * \brief Destructor
	 */
	~Array();
	
	/**
	 * \fn void Push_Back(const T & _element);
	 * \param _element new element added
	 * \brief Add an element a the end of the array
	 */
	void Push_Back(const T & _element);

	/**
	 * \fn void Push_Front(const T & _element);
	 * \param _element new element added
	 * \brief Add an element a the beginning of the array
	 */
	void Push_Front(const T & _element);

	/**
	 * \fn void Pop_Back();
	 * \brief remove last element
	 */
	void Pop_Back();

	/**
	 * \fn void Pop_Front();
	 * \brief remove first element
	 */
	void Pop_Front();

	/**
	 * \fn T& Back();
	 * \return last element
	 * \brief retrieve the last element of the array
	 */
	T& Back();

	/**
	 * \fn const T& Back() const;
	 * \return last element
	 * \brief retrieve the last element of the array
	 */
	const T& Back() const;

	/**
	 * \fn T& Front();
	 * \return first element
	 * \brief retrieve the first element of the array
	 */
	T& Front();

	/**
	 * \fn const T& Front() const;
	 * \return first element
	 * \brief retrieve the first element of the array
	 */
	const T& Front() const;

	/**
	 * \fn void InsertAt(uint32 _iPos, const T & _element);
	 * \param _iPos position to insert to
	 * \param _element element to insert
	 * \brief Insert an element to the given position
	 */
	void InsertAt(uint32 _iPos, const T & _element);

	/**
	 * \fn void RemoveAt(uint32 _iPos);
	 * \param _iPos position of the element to remove
	 * \brief remove an element from the array
	 */
	void RemoveAt(uint32 _iPos);

	/**
	 * \fn void Insert(Iterator _it, const T & _element);
	 * \param _it place where the element is inserted
	 * \param _element element to insert
	 * \brief insert an element to the position given by the iterator
	 */
	void Insert(Iterator _it, const T & _element);

	/**
	 * \fn Iterator Remove(Iterator _it);
	 * \param _it place to remove an element
	 * \brief remove an element from the array
	 */
	Iterator Remove(Iterator _it);

	/**
	 * \fn void Insert(ConstIterator _it, const T & _element);
	 * \param _it place where the element is inserted
	 * \param _element element to insert
	 * \brief insert an element to the position given by the iterator
	 */
	void Insert(ConstIterator _it, const T & _element);

	/**
	 * \fn Iterator Remove(ConstIterator _it);
	 * \param _it place to remove an element
	 * \brief remove an element from the array
	 */
	ConstIterator Remove(ConstIterator _it);

	/**
	 * \fn Iterator Begin();
	 * \return a pointer on the first element
	 * \brief Begin iterator
	 */
	Iterator Begin();

	/**
	 * \fn Iterator End();
	 * \return a pointer after the last element
	 * \brief End iterator
	 */
	Iterator End();

	/**
	 * \fn ConstIterator Begin() const;
	 * \return a pointer on the first element
	 * \brief Begin iterator
	 */
	ConstIterator Begin() const;

	/**
	 * \fn ConstIterator End() const;
	 * \return a pointer after the last element
	 * \brief End iterator
	 */
	ConstIterator End() const;

	/**
	 * \fn Iterator Find(const T & _element);
	 * \param _element element to find
	 * \return iterator pointing on this element if find, otherwise returns end iterator
	 * \brief find element in array
	 */
	Iterator Find(const T & _element);

	/**
	 * \fn ConstIterator Find(const T & _element)const;
	 * \param _element element to find
	 * \return iterator pointing on this element if find, otherwise returns end iterator
	 * \brief find element in array
	 */
	ConstIterator Find(const T & _element)const;

	/**
	 * \fn Array<T> & operator = (const Array<T> & _tArray);
	 * \param _tArray array to copy
	 * \return (*this) with new values
	 * \brief operator =
	 */
	Array<T> & operator = (const Array<T> & _tArray);

	/**
	 * \fn template < class S > friend bool operator == (const Array<S> & _tArray1, const Array<S> & _tArray2);
	 * \param _tArray1 array1
	 * \param _tArray2 array2
	 * \return true if array1 is equal to array2
	 * \brief operator ==
	 */
	template < class S > friend bool operator == (const Array<S> & _tArray1, const Array<S> & _tArray2);

	/**
	 * \fn template < class S > friend bool operator != (const Array<S> & _tArray1, const Array<S> & _tArray2);
	 * \param _tArray1 array1
	 * \param _tArray2 array2
	 * \return true if array1 is not equal to array2
	 * \brief operator !=
	 */
	template < class S > friend bool operator != (const Array<S> & _tArray1, const Array<S> & _tArray2);
};


template< class T >
Array<T>::Array()
: ArrayBase<T>()
{
}

template< class T >
Array<T>::Array(uint32 _iSize)
: ArrayBase<T>(_iSize)
{
}

template< class T >
Array<T>::Array(const Array<T> & _tArray)
: ArrayBase<T>()
{
	ArrayBase<T>::_Clone(_tArray.m_tArray, _tArray.m_iSize);
}

template< class T >
Array<T>::~Array()
{
}

template< class T >
void Array<T>::Push_Back(const T & _element)
{
	ArrayBase<T>::_Insert(End(), _element);
}

template< class T >
void Array<T>::Push_Front(const T & _element)
{
	ArrayBase<T>::_Insert(Begin(), _element);
}

template< class T >
void Array<T>::Pop_Back()
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call pop function on empty array");
	ArrayBase<T>::_Remove(End());
}

template< class T >
void Array<T>::Pop_Front()
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call pop function on empty array");
	ArrayBase<T>::_Remove(Begin());
}

template< class T >
T& Array<T>::Back()
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call Back() function on empty array");
	return ArrayBase<T>::m_tArray[ArrayBase<T>::m_iSize - 1];
}

template< class T >
const T& Array<T>::Back() const
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call Back() function on empty array");
	return ArrayBase<T>::m_tArray[ArrayBase<T>::m_iSize - 1];
}

template< class T >
T& Array<T>::Front()
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call Front() function on empty array");
	return ArrayBase<T>::m_tArray[0];
}

template< class T >
const T& Array<T>::Front() const
{
	LOG_ASSERT((ArrayBase<T>::m_iSize > 0), "Call Front() function on empty array");
	return ArrayBase<T>::m_tArray[0];
}

template< class T >
void Array<T>::InsertAt(uint32 _iPos, const T & _element)
{
	LOG_ASSERT( (_iPos <= ArrayBase<T>::m_iSize), "Insert position invalid" );
	_Insert(ArrayBase<T>::m_tArray+_iPos, _element);
}

template< class T >
void Array<T>::RemoveAt(uint32 _iPos)
{
	LOG_ASSERT( (_iPos < ArrayBase<T>::m_iSize), "Insert position invalid" );
	ArrayBase<T>::_Remove(ArrayBase<T>::m_tArray+_iPos);
}

template< class T >
void Array<T>::Insert(Iterator _it, const T & _element)
{
	ArrayBase<T>::_Insert(_it, _element);
}

template< class T >
typename Array<T>::Iterator Array<T>::Remove(Iterator _it)
{
	return ArrayBase<T>::_Remove(_it);
}

template< class T >
void Array<T>::Insert(ConstIterator _it, const T & _element)
{
	ArrayBase<T>::_Insert(_it, _element);
}

template< class T >
typename Array<T>::ConstIterator Array<T>::Remove(ConstIterator _it)
{
	return ArrayBase<T>::_Remove(_it);
}
 
template< class T >
typename Array<T>::Iterator Array<T>::Begin()
{
	return ArrayBase<T>::m_tArray;
}

template< class T >
typename Array<T>::Iterator Array<T>::End()
{
	return (ArrayBase<T>::m_tArray + ArrayBase<T>::m_iSize);
}

template< class T >
typename Array<T>::ConstIterator Array<T>::Begin() const
{
	return ArrayBase<T>::m_tArray;
}

template< class T >
typename Array<T>::ConstIterator Array<T>::End() const
{
	return (ArrayBase<T>::m_tArray + ArrayBase<T>::m_iSize);
}

template< class T >
typename Array<T>::Iterator Array<T>::Find(const T & _element)
{
	return ArrayBase<T>::_Find(_element);
}

template< class T >
typename Array<T>::ConstIterator Array<T>::Find(const T & _element)const
{
	return ArrayBase<T>::_Find(_element);
}
template< class T >
Array<T> & Array<T>::operator = (const Array<T> & _tArray)
{
	ArrayBase<T>::_Clone(_tArray.m_tArray, _tArray.m_iSize);
	return (*this);
}

/**
 * \fn template < class S > bool operator == (const Array<S> & _tArray1, const Array<S> & _tArray2);
 * \param _tArray1 array1
 * \param _tArray2 array2
 * \return true if array1 is equal to array2
 * \brief operator ==
 */
template< class S >
bool operator == (const Array<S> & _tArray1, const Array<S> & _tArray2)
{
	uint32 iSize1 = _tArray1.Size();
	if( iSize1 == _tArray2.Size() )
	{
		for(uint32 i=0; i < iSize1; ++i)
			if( _tArray1[i] != _tArray2[i] )
				return false;
		
		return true;
	}
	
	return false;
}

/**
 * \fn template < class S > bool operator != (const Array<S> & _tArray1, const Array<S> & _tArray2);
 * \param _tArray1 array1
 * \param _tArray2 array2
 * \return true if array1 is not equal to array2
 * \brief operator !=
 */
template< class S >
bool operator != (const Array<S> & _tArray1, const Array<S> & _tArray2)
{
	return !(_tArray1 == _tArray2);
}

#endif // _ARRAY_
